/*
* Darwin.h
*
*  Created on: Nov 14, 2009
*      Author: OD
*/

#ifndef DARWIN_H_
#define DARWIN_H_
#include "Creature.h"
#include "Coordinate.h"
#include <vector>
#include <cstdio>
#include <cstdlib>

/**
* The main driver. Controls almost all functions.
*/
struct Darwin{
private:

       std::vector< std::vector< Creature* > > map;

       std::vector<Coordinate> myList;//1D array

       //int mySize;

public:
	/**
	* constructor: create empty row x col map 
	* @param row number of rows
	* @param col number of collumns
	*/
       Darwin(int row, int col){
			Creature* x=0;
                       std::vector< Creature* > temp(row,x);
			std::vector< std::vector< Creature* > > map2(col,temp);
			map=map2;
	}


	/**
 	* Start the Creature madness!!
	* @param turns number of turns to execute
 	*/
	void eval(int turns) {
		int turn=0;
		print(turn);
		while(turn<turns){
			
			//puts("here");
			mySort();
			for(int x=0;x<myList.size(); ++x)
			{
				Coordinate coord=myList[x];
				Creature* c=map[coord.getX()][coord.getY()];
				//printf("faceing %d %d %d\n",c->getDirection(),coord.getX(),coord.getY());
				runInstructions((c->getSpecies())->getInstructions(),*c,x);
			}
			++turn;
			if(turns>100 && turn%100==0)
				print(turn);
			else if(turns<100)
				print(turn);
		}
	}

	/**
 	* Calculates the next spot given a location and direction
 	* @param I vector of instructions
	* @param c Creature executing
	* @param index index for c's Coordinates in myList
 	*/
	//hop=0, infect=1, go=2, left=3, right=4, ifEmpty=5, ifEnemy=6, ifRandom=7, ifWall=8
	void runInstructions(std::vector<Instruction>& I,Creature& c, int index) {
		//puts("heres");
		int& pc=c.getPC();
		Instruction next=I[pc];
		while(true)
		{
			//printf("go %d %d %d\n",pc,I[pc].getOp(),I[pc].getStep());
			next=I[pc];
			switch(next.getOp()) {
			case 0://hop
				if(ifEmpty(c,myList[index])&& !ifWall(c,myList[index])){
					hop(c,myList[index]);
					
				}
				++pc;
				return;

			case 1://infect
				if(ifEnemy(c,myList[index]))
					infect(c,myList[index]);
				++pc;
				return;

			case 2://go
				pc=next.getStep();
				break;

			case 3://left
				c.setDirection(1);
				++pc;
				return;

			case 4://right
				c.setDirection(2);
				++pc;
				return;

			case 5://ifempty
				if(ifEmpty(c,myList[index]))
					pc=next.getStep();
				else
					++pc;
				break;
			case 6://ifenemy
				if(ifEnemy(c,myList[index]))
					pc=next.getStep();
				else
					++pc;
				break;
			case 7://ifRandom
				if(ifRandom()){
					pc=next.getStep();
					//printf("pc after right %d",pc);
					//puts("dafsdfasdfsadfasdfsdafsdafsadfsadfsadfsdaf");
				}
				else{
					
					++pc;	}
				break;
			case 8://ifWall
				if(ifWall(c,myList[index]))
					pc=next.getStep();
				else
					++pc;
				break;
			
			default:
				printf("Error: bad op %d %d %c",next.getOp(),pc,c.getSpecies()->getName());
				
				throw "Bad Instruction!";
			}
		}

	}
	
	/**
 	* moves Creature c one space
 	* @param c Creature to move
	* @param coord Coordinate of c
 	*/

	void hop(Creature& c, Coordinate& coord){
		//printf("hop %d\n",c.getDirection());
		//map[coord.getY()][coord.getX()]=0; //make old spot empty
		map[coord.getX()][coord.getY()]=0; //make old spot empty
		coord=getNextSpot(c.getDirection(), coord);
		//map[coord.getY()][coord.getX()]=&c; //place in new spot
		map[coord.getX()][coord.getY()]=&c; //place in new spot
	}
	
	/**
 	* infects the Creature in front of c
 	* @param c Creature doing the infecting
	* @param coord Coordinate of c
 	*/
	void infect(Creature& c, Coordinate& coord){
		//puts("infect!!");
		Coordinate next=getNextSpot(c.getDirection(), coord);
		map[next.getX()][next.getY()]->infect(c.getSpecies()); 
	}

	/**
 	* Calculates the next spot given a location and direction
 	* @param direction direction facing
	* @param coord Original Coordinate
	* @return next spot
 	*/
	Coordinate getNextSpot(int direction, Coordinate coord) {
		switch(direction) {
			case 0://north
				coord.setY(coord.getY()-1);
				break;
			case 1://east
				coord.setX(coord.getX()+1);
				break;
			case 2://south
				coord.setY(coord.getY()+1);
				break;
			case 3://west
				coord.setX(coord.getX()-1);
		}
		return coord;
	}

	/**
 	* Checks if coord is the map boundaries
	* @param coord location to be checked
	* @return true if coord is on the map
 	*/
	bool isValidCoords(Coordinate& coord) {
		return (coord.getX() >= 0) && (coord.getY() >= 0) && (coord.getX() < map.size()) && (coord.getY()<map[0].size());
	}

	
	/**
 	* Checks if enemy Creature is in front of c
 	* @param c Creature attacking
	* @param coord c's Coordinate
	* @return true if enemy is in next square
 	*/
	bool ifEnemy(Creature& c, Coordinate& coord){
		//puts("check enemy");
		Coordinate next=getNextSpot(c.getDirection(),coord);
		if(!isValidCoords(next)){
			//puts("check enemy out of bounds");
			return false;}
		if(map[next.getX()][next.getY()]==0){
			//puts("check enemy empty");
			return false;}
		return c.getSpecies()!=map[next.getX()][next.getY()]->getSpecies();
	}

	/**
 	* Checks if there is a wall in front
 	* @param c Creature
	* @param coord c's Coordinate
	* @return true if c is facing wall
 	*/
	bool ifWall(Creature& c, const Coordinate& coord){
		Coordinate next=getNextSpot(c.getDirection(),coord);
		return !isValidCoords(next);
	}

	/**
 	* Checks if the next the spot is empty
 	* @param c creature
	* @param coord c's Coordinate
	* @return true if next spot is empty
 	*/
	bool ifEmpty(Creature& c, const Coordinate& coord) { 
		Coordinate next=getNextSpot(c.getDirection(),coord);
		if(!isValidCoords(next))
			return false;
		return map[next.getX()][next.getY()]==0;
	}


	/**
 	* Random boolean method
	* @return true randomly
 	*/
	bool ifRandom(){
		int r=std::rand();
		bool t=r%2;
		return t;
	}
	
	//this is such a lazy wasteful sort routine :)
	/**
 	* Calculates the next spot given a location and direction
 	* @param direction direction facing
	* @param coord Original Coordinate
	* @return next spot
 	*/
	void mySort() {
		std::vector<Coordinate> temp=myList;//copy myList
		int position=0;
		
		while(temp.size()){
			int index=0;
			Coordinate c=temp[0];
			for(int j=0;j<temp.size();++j){
				if(c>temp[j]){
					index=j;
					c=temp[j];
				}	
			}
			myList[position]=temp[index];
			++position;
			temp.erase(temp.begin()+index);
		}

	}

	/**
 	* Helper method
 	*/

	Coordinate getMyList(int i){
               return myList[i];
       	}
	
	/**
 	* Get Creature at given location
 	* @param row map row
	* @param col map collumn 
	* @return Creature at location
 	*/
       	Creature getCreature(int r, int c){
               return *map[r][c];
       	}

	/**
 	* Adds c to the map. Adding the same creature twice will cause silly behavoir. Adding to the same spot is fine though.
 	* @param row map row
	* @param col map collumn 
	* @param c Creature to add to board
 	*/
	void setCreature(int row, int col, Creature& c){ //makes a copy of the creature passed in
		if(map[col][row]==0){ //if empty spot
			Coordinate coord(col,row);
			myList.push_back(coord);
		}
		map[col][row]=&c; 
		
	}
	void setCreature(int row, int col, Creature* c){ //makes a copy of the creature passed in
		if(map[col][row]==0){ //if empty spot
			Coordinate coord(col,row);
			myList.push_back(coord);
		}
		map[col][row]=c; 
		
	}
	

	//void clear() {

	//}
	
	/**
 	* print out turn info
	* @return turn eval turn
 	*/
	void print(int turn){
		printf("Turn = %d.\n", turn);
		printBoard();
		printf("\n");
	}
	
	/**
 	* Prints map
 	*/
	void printBoard() {
		
		printf("  ");
		for(int c=0;c<map.size();++c){
			printf("%d",c%10);
		}
		printf("\n");
		for(int r=0;r<map[0].size();++r){
			printf("%d ", r%10);
			
			for(int c=0;c<map.size();++c){
				if(map[c][r]==0)
					printf(".");
				else
					printf("%c",(map[c][r]->getSpecies())->getName());
			}
			printf("\n");
		}
	}


	
};

#endif //DARWIN_H_
